import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from '@tiptap/pm/state';
import { Decoration, DecorationSet } from '@tiptap/pm/view';

import { Node } from 'prosemirror-model';
import tippy from "tippy.js";

import { InnerEditor } from '@/core/UAIEditor.ts';

const SpellCheckPluginKey = new PluginKey('SpellCheck');

export default Extension.create({
  name: 'SpellCheck',

  addOptions() {
    return {
      enabled: true,
      checkOnType: true,
      highlightColor: '#ff6b6b',
      underlineStyle: 'wavy'
    };
  },

  addProseMirrorPlugins() {
    const extension = this;
    return [
      new Plugin({
        key: SpellCheckPluginKey,
        state: {
          init() {
            return DecorationSet.empty;
          },
          apply(tr, decorationSet) {
            if (!extension.options.enabled) {
              return DecorationSet.empty;
            }

            decorationSet = decorationSet.map(tr.mapping, tr.doc);

            if (tr.docChanged || tr.selectionSet) {
              function checkDocument(doc: Node) {
                const decorations: Decoration[] = [];
                const options = extension.options;

                doc.descendants((node, pos) => {
                  if (node.isText && node.text) {
                    const text = node.text;
                    const uaiEditorOptions = (extension.editor as InnerEditor).uaiEditor.options;
                    const suggest = uaiEditorOptions.spellCheck?.suggest || {}
                    const exclude = uaiEditorOptions.spellCheck?.exclude || {}

                    // 检查错别字
                    for (const [typo, correct] of Object.entries(suggest)) {
                      // Skip if typo is in exclude list
                      if (exclude[typo]) continue;

                      const regex = new RegExp(typo.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
                      let match;

                      while ((match = regex.exec(text)) !== null) {
                        const from = pos + match.index;
                        const to = from + match[0].length;

                        decorations.push(
                          Decoration.inline(from, to, {
                            class: 'spell-error',
                            style: `text-decoration: underline ${options.underlineStyle}; text-decoration-color: ${options.highlightColor};`,
                            message: `建议使用：${correct}`
                          }),
                          Decoration.widget(to, () => {
                            const span = document.createElement('span');
                            span.className = 'spell-suggest';
                            span.innerHTML = correct;

                            function createContainer() {
                              const container = document.createElement('div');
                              container.classList.add("spell-action-container");

                              const reject = document.createElement("button");
                              reject.classList.add("spell-reject-button");
                              reject.innerHTML = "拒绝";
                              reject.onclick = () => {
                                uaiEditorOptions.spellCheck!.exclude![typo] = correct;
                              }

                              const accept = document.createElement("button");
                              accept.classList.add("spell-accept-button");
                              accept.innerHTML = "接受";
                              accept.onclick = () => {
                                const tr = extension.editor.state.tr.replaceWith(from, to, extension.editor.schema.text(correct));
                                extension.editor.view.dispatch(tr.scrollIntoView());
                              };

                              container.appendChild(reject);
                              container.appendChild(accept);
                              return container;
                            }

                            tippy(span, {
                              content: createContainer(),
                              appendTo: span,
                              placement: 'top',
                              interactive: true,
                              arrow: true
                            });
                            return span;
                          })
                        );
                      }
                    }
                  }
                });

                return DecorationSet.create(doc, decorations);
              }
              decorationSet = checkDocument(tr.doc);
            }

            return decorationSet;
          }
        },
        props: {
          decorations(state) {
            return this.getState(state);
          }
        }
      })
    ];
  },
});
